#include "XXqtDefine.h"
#include <QWidget>
#include <QPushButton>
#include <QCoreApplication>
#include <QVariant>

#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonArray>
#include <QGraphicsDropShadowEffect>

#include <QApplication>
#include <QDateTime>
#include <QUuid>
#include <QCryptographicHash>

using namespace XXqtDefine;

void XXqtDefine::safeDirInFilePath(const QString &filePath){
    QFileInfo info(filePath);
    if(info.dir().exists()){
        return;
    }
    QDir().mkpath(info.absolutePath());
}
QDir XXqtDefine::safeDir(const QString &path){
    QDir dir(path);
    if(!dir.exists()){
        QDir().mkpath(path);
    }
    return dir;
}

void XXqtDefine::copyDir(const QString &dirPath, const QString &newDirPath, std::function<void(QString from, QString to, bool succeed, QString msg)> block){
    QDir dir(dirPath);
    if(!dir.exists()){
        if(nullptr!=block) block(dirPath, newDirPath, false, "FROM dir is not exists");
        return;
    }
    QDir newDir = safeDir(newDirPath);

    auto items = dir.entryInfoList();
    foreach(auto item, items){
        if(item.fileName() == "." || item.fileName() == ".."){
            continue;
        }

        QString path = dirPath+"/"+item.fileName();
        QString newPath = newDirPath+"/"+item.fileName();
        if(item.isFile()){
            if(QFileInfo(newPath).exists()){
                newDir.remove(item.fileName());
            }
            if(!QFile::copy(path,newPath)){
                if(nullptr!=block) block(path,newPath,false,"");
            }
            else{
                if(nullptr!=block) {
                    block(path,newPath,true,"");
                }
            }
        }
        else if(item.isDir()){
            copyDir(path, newPath);
        }
        else{

        }
    }
}
void XXqtDefine::copy(const QString &path, const QString &newPath){
    QFileInfo info(path);
    if(!info.exists()){
        return;
    }

    QFileInfo newInfo(newPath);
    if(newInfo.exists()){
        bool ret = false;
        if(newInfo.isDir()){
            ret = QDir(newPath).removeRecursively();
        }
        else if(newInfo.isFile()){
            ret = QFile(newPath).remove();
        }
        else{

        }
    }

    if(info.isDir()){
        copyDir(path, newPath);
    }
    else if(info.isFile()){
        QFile::copy(path,newPath);
    }
    else {

    }
}
QString XXqtDefine::searchFile(const QString &dirPath, const QString &fileName){
    QFileInfo fileInfo(dirPath);
    if(!fileInfo.exists() || !fileInfo.isDir()){
        return "";
    }

    QDir dir(dirPath);
    foreach(auto file, dir.entryInfoList(QDir::Files)){
        if(file.fileName()==fileName){
            return file.absoluteFilePath();
        }
    }

    foreach (auto subDir, dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
        QString path = searchFile(subDir.absoluteFilePath(),fileName);
        if(!path.isEmpty()){
            return path;
        }
    }
    return "";
}

QString XXqtDefine::file_md5(const QString &path){
    QFile file(path);
    if(!file.open(QIODevice::ReadOnly)){
        return "";
    }

    QByteArray bytes;
    QCryptographicHash hash(QCryptographicHash::Md5);
    while(1){
        QByteArray t = file.read(1*1024*1024);
        if(t.isEmpty()){
            break;
        }
        hash.addData(t);
    }
    bytes = hash.result();
    file.close();
    return bytes.toHex();
}

QString XXqtDefine::getSystemDirPath(){
    return QDir::homePath() + "/." + QCoreApplication::applicationName();
}
QString XXqtDefine::getAppDirPath(){
    return QCoreApplication::applicationDirPath() + "/.app";
}

void XXqtDefine::modelWindow(QWidget &widget){
    widget.setWindowModality(Qt::ApplicationModal);
    widget.setAttribute(Qt::WA_DeleteOnClose);
    widget.setWindowFlags(Qt::Window);
}
void XXqtDefine::widgetShadowEffect(QWidget *widget,int radius, QColor color){
    QGraphicsDropShadowEffect * effect = new QGraphicsDropShadowEffect(widget);
    effect->setOffset(0, 0);
    effect->setColor(color);
    effect->setBlurRadius(radius);
    widget->setGraphicsEffect(effect);
}

void XXqtDefine::messageBox(const QString &title,const QString &message, QMessageBox::Icon type, const QString &button){
    QMessageBox mb(title,
                   message,
                   type,
                   QMessageBox::Yes | QMessageBox::Default,
                   QMessageBox::NoButton,
                   QMessageBox::NoButton);
    mb.button(QMessageBox::Yes)->setText(button);
    mb.exec();
}
void XXqtDefine::messageBox(const QString &title, const QString &message, QMessageBox::Icon type, const QStringList &buttons, std::function<void(QString button)> block){
    QMessageBox::StandardButton btn1 = buttons.count()>0?QMessageBox::Yes:QMessageBox::NoButton;
    QMessageBox::StandardButton btn2 = buttons.count()>1?QMessageBox::No:QMessageBox::NoButton;
    QMessageBox::StandardButton btn3 = buttons.count()>2?QMessageBox::Cancel:QMessageBox::NoButton;

    QMessageBox mb(title,
                   message,
                   type,
                   btn1 | QMessageBox::Default,
                   btn2,
                   btn3);
    if(buttons.count()>0) mb.button(QMessageBox::Yes)->setText(buttons[0]);
    if(buttons.count()>1) mb.button(QMessageBox::No)->setText(buttons[1]);
    if(buttons.count()>2) mb.button(QMessageBox::Cancel)->setText(buttons[2]);
    int ret = mb.exec();
    if(ret == QMessageBox::Yes){
        block(buttons[0]);
    }
    else if(ret == QMessageBox::No){
        block(buttons[1]);
    }
    else if(ret == QMessageBox::Cancel){
        block(buttons[2]);
    }
    else{
        block("");
    }
}
void XXqtDefine::messageBoxInfo(const QString &title, const QString &message, const QString &button){
    messageBox(title,message,QMessageBox::Icon::Information,button);
}
void XXqtDefine::messageBoxQuestion(const QString &title, const QString &message, const QStringList &buttons, std::function<void(QString button)> block){
    messageBox(title,message,QMessageBox::Icon::Question,buttons,block);
}
void XXqtDefine::messageBoxCritical(const QString &title, const QString &message, const QString &button){
    messageBox(title,message,QMessageBox::Icon::Critical,button);
}

QString XXqtDefine::getRandomString(int length){
    static uint index = 0;
    index++;
    std::srand(index+static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));

    const char chrs[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    int chrs_size = sizeof(chrs);

    char* ch = new char[length + 1];
    memset(ch, 0, length + 1);
    int randomx = 0;
    for (int i = 0; i < length; ++i)
    {
        randomx= std::rand() % (chrs_size - 1);
        ch[i] = chrs[randomx];
    }

    QString ret(ch);
    delete[] ch;
    return ret;
}
QString XXqtDefine::getRawUUID(){
    QString uuid = QUuid::createUuid().toString();
    return uuid.replace('-',"").replace('{',"").replace('}',"");
}

/* QColor */
QColor XXqtDefine::colorFromHexString(const QString &hex){
    if(!hex.startsWith('#')){
        return QColor();
    }
    QString s = hex.right(hex.length()-1);
    int r,g,b;
    int a = 0xff;
    if(s.length() == 6){
        r = s.left(2).toInt(nullptr,16);
        g = s.mid(2,2).toInt(nullptr,16);
        b = s.right(2).toInt(nullptr,16);
    }
    else if (s.length() == 8) {
        r = s.left(2).toInt(nullptr,16);
        g = s.mid(2,2).toInt(nullptr,16);
        b = s.mid(4,2).toInt(nullptr,16);
        a = s.right(2).toInt(nullptr,16);
    }
    else{
        return QColor();
    }
    return QColor(r,g,b,a);
}
QString XXqtDefine::colorToHexString(const QColor &color){
    QString a = QString("%1").arg(color.alpha(),2,16,QChar('0'));
    QString r = QString("%1").arg(color.red(),2,16,QChar('0'));
    QString g = QString("%1").arg(color.green(),2,16,QChar('0'));
    QString b = QString("%1").arg(color.blue(),2,16,QChar('0'));
    return "#"+a+r+g+b;
}

/* Json */
QString XXqtDefine::variantToJsonString(const QVariant &var,bool indented){
    QJsonDocument jDoc;
        if(var.canConvert<QVariantMap>()){
            jDoc = QJsonDocument(QJsonObject::fromVariantMap(var.toMap()));
        }
        else if(var.canConvert<QVariantList>()){
            jDoc = QJsonDocument(QJsonArray::fromVariantList(var.toList()));
        }
        else{
            return "";
        }
        return QString::fromUtf8(jDoc.toJson(indented?QJsonDocument::Indented:QJsonDocument::Compact));
}
bool XXqtDefine::variantToJsonFile(const QVariant &var, const QString &path, bool indented){
    QString jsonString = variantToJsonString(var,indented);
    if(jsonString.isEmpty()){
        return false;
    }
    XXqtDefine::safeDirInFilePath(path);
    QFile file(path);
    if(file.exists()){
        file.resize(0);
    }
    if(!file.open(QIODevice::ReadWrite)){
        return false;
    }
    file.write(jsonString.toUtf8());
    file.close();
    return true;
}
QVariant XXqtDefine::variantFromJsonString(const QString &json){
    QJsonParseError error;
    auto jsonDoc = QJsonDocument::fromJson(json.toUtf8(),&error);
    return jsonDoc.toVariant();
}
QVariant XXqtDefine::variantFromJsonBytes(const QByteArray &json){
    QJsonParseError error;
    auto jsonDoc = QJsonDocument::fromJson(json,&error);
    return jsonDoc.toVariant();
}
QVariant XXqtDefine::variantFromJsonFile(const QString &path){
    QFile file(path);
    if(!file.exists() || !file.open(QIODevice::ReadOnly)){
        return QVariant();
    }

    QByteArray bytes = file.readAll();
    return variantFromJsonBytes(bytes);
}

/* QSS */
bool XXqtDefine::qssLoad(QApplication *app, const QString &path){
    auto bytes = qssRead(path);
    if(bytes.isEmpty()){
        return false;
    }
    app->setStyleSheet(bytes);
    return true;
}
bool XXqtDefine::qssLoad(QWidget *widget, const QString &path){
    auto bytes = qssRead(path);
    if(bytes.isEmpty()){
        return false;
    }
    widget->setStyleSheet(bytes);
    return true;
}
QByteArray XXqtDefine::qssRead(const QString &path){
    QFile file(path);
    if(!file.exists() || !file.open(QIODevice::ReadOnly)){
        return QByteArray();
    }

    QMap<QByteArray,QByteArray> varMap;
    bool varBegin = false;

    QByteArray bytes;
    while(!file.atEnd()){
        QByteArray line = file.readLine();
        if(line == "#varbegin\r\n"){
            varBegin = true;
            continue;
        }
        if(line == "#varend\r\n"){
            bytes += file.readAll();
            break;
        }
        if(varBegin){
            QString lineString = QString::fromUtf8(line);
            QString key = lineString.section(':',0,0);
            QString value = lineString.section(':',1);
            if(key.isEmpty() || value.isEmpty()){
                continue;
            }
            key = "@"+key+"@";
            value = value.left(value.length()-2);
            varMap.insert(key.toUtf8(),value.toUtf8());
        }
        else{
           bytes += line;
        }
    }
    file.close();

    for (auto iter=varMap.begin(); iter!=varMap.end(); iter++) {
        bytes.replace(iter.key(),iter.value());
    }
    return bytes;
}
